package com.xianguo.datacenter.download.Thread;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

public class DownloadHttpThread extends Download implements Runnable{

	private int lastProgres;//文件上次下载长度
	private int isOneStart;//是否启动过，交由控制核心控制启动
	private int isstart;//控制网速计算,以及是否下载完成 0未完成，1完成
	private String cookie;//要发送的cookie
	private int thread = 16;//启动多少个线程下载
	private int accessThread;//已经下载完成的线程
	private List<downloadThread> downloadMiniThread = new ArrayList<DownloadHttpThread.downloadThread>();//mini子线程的集合
	private DecimalFormat df=new DecimalFormat("0.00");//格式化进度
	
	public DownloadHttpThread(){
		
	}
	
	@Override
	public void run() {
		isOneStart = 1;
		System.out.println("下载中:"+path+"\\\\"+fileName);
        InputStream inputStream = null;
        RandomAccessFile randomAccessFile = null;
        try {
            HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.setConnectTimeout(10 * 1000);
            urlConnection.setRequestProperty("accept", "*/*");
            urlConnection.setRequestProperty("connection", "Keep-Alive");
            urlConnection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            if (null != cookie) {
            	urlConnection.setRequestProperty("Cookie", cookie);
            }
            File file = new File(path+"\\"+fileName);
            if (!file.getParentFile().exists())
                file.getParentFile().mkdir();
            if (file.exists())
                file.delete();
            file.createNewFile();
            int responseCode = urlConnection.getResponseCode();
            if (responseCode >= 200 && responseCode < 300) {
                inputStream = urlConnection.getInputStream();
                progres = 0; //用于保存当前进度（具体进度）
                maxProgres = urlConnection.getContentLength();//获取文件
                randomAccessFile = new RandomAccessFile(file, "rwd");
                randomAccessFile.setLength(maxProgres);//设置文件大小
                unitProgress = 0.00; //用于保存当前进度(1~100%)
                isstart = 0;
                speed();//开启网速计算
                
                int progresCount = maxProgres;
                if(maxProgres%thread!=0){
                	progresCount = maxProgres - (maxProgres%thread);//为了避免出现小数，要保证能被整除
                }
                
                int countSize = progresCount/thread;//计算每一块的大小
                for(int i = 0;i < thread;i++){//启动多线程下载
                	downloadThread downloads = new downloadThread();
                	downloads.setFile(file);
                	if(i!=(thread-1)){
                		downloads.setStartIndex(i * countSize);
                		downloads.setEndIndex((i+1) * countSize - 1);
                	}else{
                		downloads.setStartIndex(i * countSize);
                		downloads.setEndIndex(maxProgres);
                	}
                	downloads.setMiniUID(UUID.randomUUID().toString());
                	downloadMiniThread.add(downloads);
                	new Thread(downloads).start();//线程下载启动
                }
                
                while(true){
                	if(accessThread==thread){
                    	break;
                	}else{
                		for(downloadThread item : downloadMiniThread){
                			if(downloadState == DownloadState.DOWNLOADING){//如果线程处于正在下载状态。启动所有暂停的子线程
                				if(item.getDownloadMiniState()==1){
                					new Thread(item).start();//线程下载恢复
                				}
                				if(item.getDownloadMiniState()==3){
                					System.out.println("UID:"+item.getMiniUID()+"正在重试错误的下载线程");
                					new Thread(item).start();//错误线程下载恢复
                				}
                			}
                		}
                		Thread.sleep(1000*5);
                	}
                }
                

                isstart=1;//停止网速计算
                System.out.println("下载完成:"+path+"\\\\"+fileName);
                
            } else {
                System.out.println("服务器异常:"+path+"\\\\"+fileName);
            }
        }catch(IOException e){
        	System.out.println("流文件异常:"+path+"\\\\"+fileName);
        	e.printStackTrace();
        } catch (InterruptedException e) {
        	e.printStackTrace();
			System.out.println("线程休息异常，线程ID:"+UID);
		}finally {
            if (null != inputStream) {
            	close(inputStream);
            }
            if (null != randomAccessFile) {
            	close(randomAccessFile);
            }
            DownloadUtil.removeDownloadThread(UID);//移除下载列表
        }
	}
	
	public class downloadThread implements Runnable{
		private String miniUID;//当前子线程id
		private int startIndex;//文件块下载开始位置
		private int endIndex;//文件块下载结束位置
		private File file;//要写入的文件
		private int length;//以下载长度，断点续传时使用
		private int downloadMiniState;//当前线程状态0正在下载，1暂停下载，2完成下载，3下载错误 准备重试
		@Override
		public void run() {
			RandomAccessFile accessFile = null;
			InputStream is = null;
			try {
				downloadMiniState = 0;
                System.out.println(fileName+"http下载子线程:从"+(startIndex+length)+"---"+endIndex+"开始下载");
				HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
				urlConnection.setRequestMethod("GET");
				urlConnection.setConnectTimeout(10 * 1000);
				urlConnection.setRequestProperty("accept", "*/*");
				urlConnection.setRequestProperty("connection", "Keep-Alive");
				urlConnection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
				urlConnection.setRequestProperty("Range", "bytes="+(startIndex+length)+"-"+endIndex);  
				if (null != cookie) {
					urlConnection.setRequestProperty("Cookie", cookie);
				}
				int code = urlConnection.getResponseCode();  
	            if(code == 206){//请求部分数据 
	            	System.out.println("================线程UID"+miniUID+"以下载长度:"+length+"==============");
	            	is = urlConnection.getInputStream();
	            	accessFile = new RandomAccessFile(file,"rwd");
	            	accessFile.seek(startIndex+length);
	            	byte[] buffer = new byte[4096];  
	                int len = -1;  
	                while((len = is.read(buffer)) != -1){
	                	if(downloadState == DownloadState.DOWNLOADING){
	                		accessFile.write(buffer, 0, len);
	                		AddProgres(len);//更新进度
	                		length+=len;//记录下载长度
	                	}else{
	                		downloadMiniState=1;
	                		break;
	                	}
	                }
	                if(downloadMiniState == 0){
	                	System.out.println(fileName+"http下载子线程:从"+startIndex+"---"+endIndex+"下载完成");
	    	            synchronized (downloadThread.class) {
	    	            	accessThread++;//记录当前线程下载完成
	    				}
		                downloadMiniState = 2;//记录当前线程下载完成
		                removeDownloadThread(this.miniUID);
	                }else{
	                	System.out.println(fileName+"http下载子线程:从"+startIndex+"---"+endIndex+"暂停成功");
	                }
	            }else{
	            	System.out.println(fileName+"部分数据请求失败 下载长度为，正在重试");
	            	downloadMiniState=3;
	            }
				
			} catch (MalformedURLException e) {
				System.out.println(fileName+"URL格式错误 线程ID:"+miniUID);
				downloadMiniState=3;
			} catch (ProtocolException e) {
				System.out.println(fileName+"URL格式错误 线程ID:"+miniUID);
				downloadMiniState=3;
			} catch (IOException e) {
				System.out.println(fileName+"IO异常 线程ID:"+miniUID);
				downloadMiniState=3;
			} catch (Exception e) {
				System.out.println("意料之外的错误  线程ID:"+miniUID);
				downloadMiniState=3;
			}finally{
				close(accessFile);
				close(is);
			}
		}
		public int getStartIndex() {
			return startIndex;
		}
		public void setStartIndex(int startIndex) {
			this.startIndex = startIndex;
		}
		public int getEndIndex() {
			return endIndex;
		}
		public void setEndIndex(int endIndex) {
			this.endIndex = endIndex;
		}
		public File getFile() {
			return file;
		}
		public void setFile(File file) {
			this.file = file;
		}
		public int getLength() {
			return length;
		}
		public void setLength(int length) {
			this.length = length;
		}
		public int getDownloadMiniState() {
			return downloadMiniState;
		}
		public void setDownloadMiniState(int downloadMiniState) {
			this.downloadMiniState = downloadMiniState;
		}
		public String getMiniUID() {
			return miniUID;
		}
		public void setMiniUID(String miniUID) {
			this.miniUID = miniUID;
		}
		
	}
	
	public void speed() {
		if(isstart==1)
			return;
		Timer timer = new Timer(); 
		timer.schedule(new TimerTask() {
		public void run() {      
			speed=progres-lastProgres;
			lastProgres=progres;
			unitProgress = Double.parseDouble(df.format(((double)progres/maxProgres)*100));
			speed();
		}
		}, 1000);// 指定延迟1000毫秒后执行  
	}
	
	//删除一个正在下载的线程
	public synchronized void removeDownloadThread(String UID){//加上线程锁以免size混乱
		Iterator<downloadThread> iterator = downloadMiniThread.iterator();
		while (iterator.hasNext()) {
			downloadThread download = iterator.next();
			if (UID.equals(download.getMiniUID())) {
				iterator.remove();
			}
		}
	}
	
	private synchronized void downloadThreadAccess(){
		this.accessThread = this.accessThread + 1;
	}
	
	public String getCookie() {
		return cookie;
	}

	public void setCookie(String cookie) {
		this.cookie = cookie;
	}

	public List<downloadThread> getDownloadMiniThread() {
		return downloadMiniThread;
	}

	public void setDownloadMiniThread(List<downloadThread> downloadMiniThread) {
		this.downloadMiniThread = downloadMiniThread;
	}

	public int getIsOneStart() {
		return isOneStart;
	}

	public void setIsOneStart(int isOneStart) {
		this.isOneStart = isOneStart;
	}
	
}
